//  CS/CE 6378 Advanced Operating Systems
//	Fall 2012
//	Homework 3
//
//  Monitor process
#ifndef Monitor_h__
#define Monitor_h__

#include <stdio.h>
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <algorithm>
#include <list>
#include "Communication.hpp"

using namespace std;

/************************************************************************/
/*                         Util Functions                              */
/************************************************************************/

// Split string.
// From http://stackoverflow.com/questions/236129/splitting-a-string-in-c
vector<string> &split(const string &s, char delim, vector<string> &elems) {
	stringstream ss(s);
	string item;
	while(getline(ss, item, delim)) 
		elems.push_back(item);
	return elems;
}

vector<string> split(const string &s, char delim) {
	vector<string> elems;
	return split(s, delim, elems);
}

int strToInt (const string &Text )
{                              
	int result;
	return stringstream(Text) >> result ? result : 0;
}
string intToStr (int i )
{
	stringstream ss;
	ss << i;
	return ss.str();
}

void readFile(string fileName, vector< vector<string> >& content)
{
	ifstream infile(fileName.c_str(), ios::in|ios::binary); 
	if(!infile.is_open())
	{
		cerr<<"Error! can not open file "<<fileName<<" !"<<endl;
		exit(EXIT_FAILURE);
	}
	string str;
	while(getline(infile, str))
	{
		cout<<str<<endl;
		content.push_back(split(str, '\t'));
	}
	infile.close();
}

/************************************************************************/
/*                         Monitor                                 */
/************************************************************************/
class Monitor
{
public:
	Monitor():
		_terminate(false),
		_terminateNode(0),
		_clock(0),
		_numReceiveSent(0),
		_numFinished(0),
		_numSent0(0),
		_numSent1(0),
		_numSent2(0)
	{}

	bool initialize()
	{
		// Input file name.
		string configFileName="config.txt";
		cout<<"Read config file ./"<<configFileName<<endl;

		// Read input files.
		vector< vector<string> > configFile;
		readFile(configFileName, configFile);
		cout<<"=========================="<<endl;

		vector< vector<string> >::iterator itFile;
		list<Host> all;
		list<Host>::iterator itHost;
		//itFile=configFile.begin();

		for (int i = 0; i < 3; i++)
		{
			Host one;
			one.name=configFile[0][i*2];
			one.port=strToInt(configFile[0][i*2+1]);
			all.push_back(one);
			//itFile++;
		}

		_comm = Communication(all, true);

		return true;
	}

	bool release()
	{
		// Create output log file.
		ofstream outFile("events.log", ios::out|ios::binary);
		if(!outFile.is_open())
		{
			cerr<<"Error! can not create output file events.log!"<<endl;
			exit(EXIT_FAILURE);
		}

		vector<string>::iterator itEvent;
		for(itEvent=_events.begin();itEvent!=_events.end();itEvent++)
		{
			outFile<<(*itEvent)<<endl;
		}
		outFile.close();
		cout<<"Output file events.log created!"<<endl;

		return true;
	}

	void run()
	{
		while(1)
		{
			usleep(1000000);
			next();

			while(1)
			{
				usleep(10);
				Message msg;
				int s = _comm.recv(msg,_clock);
				if(s>=0)
					continue;

				//cout<<msg.data<<endl;
				string type = msg.data.substr(0,1);
				//cout<<"====== "<<msg.data<<endl;
				if(type=="m")
				{
					if(msg.data.size()>1)
					{
						string logStr=msg.data.substr(1);
						cout<<logStr<<endl;
						_events.push_back(logStr);
					}
				}
				else if(type=="s")
				{
					string str;
					str=msg.data.substr(1,1);
					_numSent0+=strToInt(str);
					str=msg.data.substr(2,1);
					_numSent1+=strToInt(str);
					str=msg.data.substr(3,1);
					_numSent2+=strToInt(str);

					if(++_numReceiveSent == 3)
					{
						sendNumMsg();
						_numReceiveSent=0;
					}
				}
				else if(type=="f")
				{
					if(++_numFinished == 3)
					{
						_numFinished=0;
						break;
					}
				}
				else if(type=="t")
				{
					if(++_terminateNode == 3)
					{
						_terminate=true;
						_terminateNode=0;
						break;
					}
				}

			}

			if(_terminate)
				break;
			_clock++;
		}

	}

	void next()
	{
		Message msg;
		msg.node=-1;
		msg.deliv=0;
		msg.data="n"+intToStr(_clock);
		_comm.send(msg);
		//cout<<"send next "<<endl;
	}

	void sendNumMsg()
	{
		Message msg;
		msg.node=-1;
		msg.deliv=0;
		msg.data="r"+intToStr(_numSent0)+intToStr(_numSent1)+intToStr(_numSent2);
		_comm.send(msg);//cout<<"sendNumMsg "<<msg.data<<endl;
		_numSent0=0;
		_numSent1=0;
		_numSent2=0;
	}

private:
	bool _terminate;
	int _terminateNode;
	int _clock;
	int _numReceiveSent;
	int _numFinished;
	int _numSent0;
	int _numSent1;
	int _numSent2;
	vector<string> _events;
	Communication _comm;
};



#endif // Monitor_h__
